package cn.turboinfo.fuyang.api.provider.common.service.impl.user;

import cn.turboinfo.fuyang.api.domain.common.service.user.UserCredentialService;
import cn.turboinfo.fuyang.api.entity.common.exception.user.UserCredentialException;
import cn.turboinfo.fuyang.api.entity.common.pojo.user.UserCredential;
import cn.turboinfo.fuyang.api.entity.common.pojo.user.UserCredentialCreator;
import cn.turboinfo.fuyang.api.entity.common.pojo.user.UserCredentialUpdater;
import cn.turboinfo.fuyang.api.provider.admin.component.security.SecurityEncoder;
import cn.turboinfo.fuyang.api.provider.admin.framework.shiro.helper.PasswordHelper;
import cn.turboinfo.fuyang.api.provider.common.repository.database.user.UserCredentialDAO;
import cn.turboinfo.fuyang.api.provider.common.repository.database.user.UserCredentialPO;
import lombok.RequiredArgsConstructor;
import net.sunshow.toolkit.core.qbean.api.request.QPage;
import net.sunshow.toolkit.core.qbean.api.request.QRequest;
import net.sunshow.toolkit.core.qbean.api.response.QResponse;
import net.sunshow.toolkit.core.qbean.helper.component.request.QBeanCreatorHelper;
import net.sunshow.toolkit.core.qbean.helper.component.request.QBeanUpdaterHelper;
import net.sunshow.toolkit.core.qbean.helper.service.impl.AbstractQServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;

@RequiredArgsConstructor
@Service
public class UserCredentialServiceImpl extends AbstractQServiceImpl<UserCredential> implements UserCredentialService {
    private final UserCredentialDAO userCredentialDAO;

    // 暂时使用和后台同一个密码
    private final PasswordHelper passwordHelper;

    private final SecurityEncoder securityEncoder;

    @Override
    public Optional<UserCredential> getById(Long id) {
        return userCredentialDAO.findById(id).map(this::convertQBean);
    }

    @Override
    public UserCredential getByIdEnsure(Long id) {
        return getById(id).orElseThrow(this.getExceptionSupplier("未找到数据, id=" + id, null));
    }

    @Override
    public List<UserCredential> findByIdCollection(Collection<Long> idCollection) {
        return convertStreamQBeanToList(userCredentialDAO.findByIdInOrderByIdDesc(idCollection).stream());
    }

    @Override
    @Transactional
    public UserCredential save(UserCredentialCreator creator) throws UserCredentialException {
        UserCredentialPO userCredentialPO = new UserCredentialPO();

        QBeanCreatorHelper.copyCreatorField(userCredentialPO, creator);

        return convertQBean(userCredentialDAO.save(userCredentialPO));
    }

    @Override
    @Transactional
    public UserCredential update(UserCredentialUpdater updater) throws UserCredentialException {
        UserCredentialPO userCredentialPO = getEntityWithNullCheckForUpdate(updater.getUpdateId(), userCredentialDAO);

        QBeanUpdaterHelper.copyUpdaterField(userCredentialPO, updater);

        return convertQBean(userCredentialPO);
    }

    @Override
    public QResponse<UserCredential> findAll(QRequest request, QPage requestPage) {
        return convertQResponse(findAllInternal(request, requestPage));
    }

    private Page<UserCredentialPO> findAllInternal(QRequest request, QPage requestPage) {
        return userCredentialDAO.findAll(convertSpecification(request), convertPageable(requestPage));
    }

    @Override
    public boolean match(Long credentialId, String credential) {
        UserCredential userCredential = getByIdEnsure(credentialId);

        String saltedCredential = passwordHelper.getCredential(credential, userCredential.getSalt());

        return StringUtils.equals(saltedCredential, userCredential.getCredential());
    }

    @Override
    public String decrypt(String encryptedCredential) {
        try {
            return securityEncoder.decrypt(encryptedCredential);
        } catch (Exception e) {
            throw getExceptionSupplier("解密加密凭据出错", e).get();
        }
    }

    @Transactional
    @Override
    public UserCredential save(Long userId, String password) {
        String salt = passwordHelper.getSalt();
        String credential = passwordHelper.getCredential(password, salt);

        UserCredentialPO po = new UserCredentialPO();
        po.setUserId(userId);
        po.setSalt(salt);
        po.setCredential(credential);

        return convertQBean(userCredentialDAO.save(po));
    }

    @Override
    public UserCredential getByUserIdEnsure(Long userId) {
        return convertQBean(userCredentialDAO.findByUserId(userId).orElseThrow(this.getExceptionSupplier("未找到数据, userId=" + userId, null)));
    }

    @Override
    public Optional<UserCredential> findUserDefault(Long userId) {
        return userCredentialDAO.findTopByUserIdOrderByIdDesc(userId).map(this::convertQBean);
    }

    @Override
    protected Supplier<? extends RuntimeException> getExceptionSupplier(String message,
                                                                        Throwable cause) {
        return () -> new UserCredentialException(message, cause);
    }
}
